package com.ibeeking.found.upms.g.rest.service.impl;

import com.ibeeking.found.common.base.BaseDO;
import com.ibeeking.found.common.config.feign.MsgFeignClient;
import com.ibeeking.found.common.config.prop.FileOssProp;
import com.ibeeking.found.common.config.secret.RsaConfig;
import com.ibeeking.found.common.constants.DefaultPathConstant;
import com.ibeeking.found.common.entity.CurrentUser;
import com.ibeeking.found.common.entity.LoginUserDTO;
import com.ibeeking.found.common.enums.LoginRoleEnum;
import com.ibeeking.found.common.enums.PublishStatusEnum;
import com.ibeeking.found.common.enums.member.MemberSexEnum;
import com.ibeeking.found.upms.api.common.dto.UserInfoDTO;
import com.ibeeking.found.upms.api.common.query.AccountLoginQuery;
import com.ibeeking.found.upms.g.rest.config.prop.UserConfigProp;
import com.ibeeking.found.upms.g.rest.service.IGmcPermissionService;
import com.ibeeking.found.upms.g.rest.service.IGmcRoleService;
import com.ibeeking.found.upms.g.rest.service.IGmcUserRoleService;
import com.ibeeking.found.upms.g.rest.service.IGmcUserService;
import com.ibeeking.found.upms.service.common.bo.GmcUserBO;
import com.ibeeking.found.upms.service.common.bo.GmcUserRoleBO;
import com.ibeeking.found.upms.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.upms.service.common.dos.UserDO;
import com.ibeeking.found.upms.service.common.param.ModifyMobileParam;
import com.ibeeking.found.upms.service.common.param.UserFindPwdParam;
import com.ibeeking.found.upms.service.common.param.UserModifyPwdParam;
import com.ibeeking.found.upms.service.common.param.GmcUserParam;
import com.ibeeking.found.upms.service.common.query.GmcUserPageQuery;
import com.ibeeking.found.upms.service.common.vo.CurrentPermissionVO;
import com.ibeeking.found.upms.service.common.vo.GmcUserPageVO;
import com.ibeeking.found.upms.service.common.vo.GmcUserVO;
import com.ibeeking.found.upms.service.mapper.UserMapper;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.exception.EncryptException;
import com.ibeeking.nematos.redis.annotation.CacheEvict;
import com.ibeeking.nematos.redis.annotation.CacheQuery;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @ClassName GmcUserServiceImpl
 * @Description 全局用户
 * @Author ibeeking
 * @Date 2020-12-01 18:36
 **/
@Service
public class GmcUserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IGmcUserService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private IGmcUserRoleService userRoleServiceImpl;
    @Resource
    private IGmcRoleService roleServiceImpl;
    @Resource
    private IGmcPermissionService permissionServiceImpl;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UserConfigProp userConfigProp;
    @Resource
    private FileOssProp fileOssProp;
    @Resource
    private RsaConfig rsaConfig;
    @Resource
    private MsgFeignClient msgClient;

    @Override
    public Page<GmcUserPageVO> pageList(GmcUserPageQuery gmcUserPageQuery) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>(UserDO.class);
        queryWrapper.orderByDesc(BaseDO::getCreateTime);

        if (null != gmcUserPageQuery.getPublishStatus()) {
            queryWrapper.eq(UserDO::getPublishStatus, gmcUserPageQuery.getPublishStatus());
        }

        if (null != gmcUserPageQuery.getCreateStartTime() || null != gmcUserPageQuery.getCreateEndTime()) {
            if (null == gmcUserPageQuery.getCreateStartTime()) {
                gmcUserPageQuery.setCreateStartTime(LocalDateTime.now());
            }
            if (null == gmcUserPageQuery.getCreateEndTime()) {
                gmcUserPageQuery.setCreateEndTime(LocalDateTime.now());
            }
            queryWrapper.between(BaseDO::getCreateTime, gmcUserPageQuery.getCreateStartTime(), gmcUserPageQuery.getCreateEndTime());
        }

        if (StringUtils.isNotBlank(gmcUserPageQuery.getMobile())) {
            queryWrapper.eq(UserDO::getMobile, gmcUserPageQuery.getMobile());
        }

        if (StringUtils.isNotBlank(gmcUserPageQuery.getEmail())) {
            queryWrapper.like(UserDO::getEmail, gmcUserPageQuery.getEmail());
        }

        if (StringUtils.isNotBlank(gmcUserPageQuery.getUserCode())) {
            queryWrapper.like(UserDO::getUserCode, gmcUserPageQuery.getUserCode());
        }

        if (StringUtils.isNotBlank(gmcUserPageQuery.getName())) {
            queryWrapper.like(UserDO::getName, gmcUserPageQuery.getName());
        }

        if (StringUtils.isNotBlank(gmcUserPageQuery.getTel())) {
            queryWrapper.like(UserDO::getTel, gmcUserPageQuery.getTel());
        }

        Page<UserDO> page = userMapper.selectPage(new Page<>(gmcUserPageQuery.getPageNum(), gmcUserPageQuery.getPageSize()), queryWrapper);
        Page<GmcUserPageVO> result = BeanUtil.convertPage(page, GmcUserPageVO.class);
        if (!Collections3.isEmpty(result.getRecords())) {
            result.getRecords().stream().forEach(userPageVO -> userPageVO.setSexName(MemberSexEnum.getDescByCode(userPageVO.getSex())));
        }
        return result;
    }

    @Override
    public Boolean add(GmcUserParam gmcUserParam) {
        IGmcUserService bean = SpringBeanUtil.getBean(IGmcUserService.class);
        //校验用户编码
        Long id1 = bean.checkUserCode(gmcUserParam.getUserCode());
        if (null != id1) {
            throw new BusinessException("当前用户编码已存在");
        }
        //校验手机号
        Long id2 = bean.checkMobile(gmcUserParam.getMobile());
        if (null != id2) {
            throw new BusinessException("当前手机号已存在");
        }

        UserDO userDO = BeanUtil.convertBean(gmcUserParam, UserDO.class);
        userDO.setCreateTime(LocalDateTime.now());
        userDO.setModifyTime(LocalDateTime.now());
        userDO.setPassword(passwordEncoder.encode(userConfigProp.getInitialPwd()));
        if (StringUtils.isNotBlank(userDO.getImage())) {
            userDO.setImage(StringUtils.removePrefixIgnoreCase(userDO.getImage(), fileOssProp.getHost()));
        }else{
            userDO.setImage(DefaultPathConstant.DEFAULT_USER_HEAD_IMAGE);
        }
        int insert = userMapper.insert(userDO);
        return insert > 0;
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.USER_INFO, key = "#gmcUserParam.id")
    @Override
    public Boolean modify(GmcUserParam gmcUserParam) {
        IGmcUserService bean = SpringBeanUtil.getBean(IGmcUserService.class);
        //校验手机号
        Long id2 = bean.checkMobile(gmcUserParam.getMobile());
        if (null != id2 && !id2.equals(gmcUserParam.getId())) {
            throw new BusinessException("当前手机号已存在");
        }
        UserDO userDO = BeanUtil.convertBean(gmcUserParam, UserDO.class);
        if (StringUtils.isNotBlank(userDO.getImage())) {
            userDO.setImage(StringUtils.removePrefixIgnoreCase(userDO.getImage(), fileOssProp.getHost()));
        }else{
            userDO.setImage(DefaultPathConstant.DEFAULT_USER_HEAD_IMAGE);
        }
        userDO.setUserCode(null);
        userDO.setModifyTime(LocalDateTime.now());
        return userMapper.updateById(userDO) > 0;
    }

    @Override
    public GmcUserVO view(Long id) {
        GmcUserBO gmcUserBO = SpringBeanUtil.getBean(IGmcUserService.class).queryById(id);
        return BeanUtil.convertBean(gmcUserBO, GmcUserVO.class);
    }

    @CacheQuery(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.USER_INFO, key = "#id")
    @Override
    public GmcUserBO queryById(Long id) {
        UserDO userDO = userMapper.selectById(id);
        Optional.ofNullable(userDO.getImage()).ifPresent(img -> {
            userDO.setImage(fileOssProp.getHost() + img);
        });
        return BeanUtil.convertBean(userDO, GmcUserBO.class);
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.USER_INFO, key = "#id")
    @Override
    public Boolean changeStatus(Long id, Integer status, Long userId) {
        UserDO userDO = new UserDO();
        userDO.setId(id);
        userDO.setPublishStatus(status);
        userDO.setModifyBy(userId);
        userDO.setModifyTime(LocalDateTime.now());
        return userMapper.updateById(userDO) > 0;
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE_G + RedisKeyConstant.USER_INFO, key = "#id")
    @Override
    public Boolean del(Long id) {
        return userMapper.deleteById(id) > 0;
    }

    @Override
    public Long checkUserCode(String userCode) {
        UserDO userDO = userMapper.selectOne(new LambdaQueryWrapper<>(UserDO.class).eq(UserDO::getUserCode, userCode));
        return null != userDO ? userDO.getId() : null;
    }

    @Override
    public Long checkMobile(String mobile) {
        UserDO userDO = userMapper.selectOne(new LambdaQueryWrapper<>(UserDO.class).eq(UserDO::getMobile, mobile));
        return null != userDO ? userDO.getId() : null;
    }

    @Override
    public Long checkEmail(String email) {
        UserDO userDO = userMapper.selectOne(new LambdaQueryWrapper<>(UserDO.class).eq(UserDO::getEmail, email));
        return null != userDO ? userDO.getId() : null;
    }

    @Override
    public LoginUserDTO queryAccountLogin(AccountLoginQuery query) {
        LambdaQueryWrapper<UserDO> queryWrapper = new LambdaQueryWrapper<>(UserDO.class);
        if (StringUtils.isNotBlank(query.getUserCode())) {
            //通过用户名/手机号/邮箱查询登录租户用户信息
            queryWrapper.and(Wrapper -> Wrapper
                    .eq(UserDO::getUserCode, query.getUserCode())
                    .or()
                    .eq(UserDO::getMobile, query.getUserCode())
                    .or()
                    .eq(UserDO::getEmail, query.getUserCode())
            );
        } else if (StringUtils.isNotBlank(query.getMobile())) {
            //通过手机号查询
            queryWrapper.eq(UserDO::getMobile, query.getMobile());
        } else if (StringUtils.isNotBlank(query.getEmail())) {
            //通过邮箱查询
            queryWrapper.eq(UserDO::getEmail, query.getEmail());
        } else {
            return null;
        }
        //queryWrapper.eq(UserDO::getPublishStatus, PublishStatusEnum.ENABLE);
        UserDO userDO = userMapper.selectOne(queryWrapper);
        if (null == userDO) {
            return null;
        }
        //判断账号状态
        LoginUserDTO loginUserDTO = BeanUtil.convertBean(userDO, LoginUserDTO.class);
        loginUserDTO.setLock(!PublishStatusEnum.ENABLE.getValue().equals(userDO.getPublishStatus()));
        loginUserDTO.setJob(PublishStatusEnum.ENABLE.getValue().equals(userDO.getJob()));
        loginUserDTO.setLoginRole(LoginRoleEnum.GLOBAL.name());
        return loginUserDTO;
    }

    @Override
    public UserInfoDTO queryLoginUser(Long userId) {
        GmcUserBO gmcUserBO = getBean().queryById(userId);
        UserInfoDTO userInfoDTO = BeanUtil.convertBean(gmcUserBO, UserInfoDTO.class);
        List<GmcUserRoleBO> gmcUserRoleBOS = userRoleServiceImpl.queryUserRoleByUserId(userId);
        userInfoDTO.setRoleIds(gmcUserRoleBOS.parallelStream().map(GmcUserRoleBO::getRoleId).collect(Collectors.toList()));
        return userInfoDTO;
    }

    @Override
    public Boolean resetPwd(Long id) {
        return modifyPassword(id, "");
    }

    @Transactional
    @Override
    public Boolean modifyPwd(Long id, UserModifyPwdParam userModifyPwdParam) {
        UserDO userDO = userMapper.selectById(id);
        String oldPwd;
        String newPwd;
        try {
            oldPwd = EncryptUtils.RsaEncrypt.decrypt(userModifyPwdParam.getOldPassword(), rsaConfig.getKeyPair().getPrivate());
            newPwd = EncryptUtils.RsaEncrypt.decrypt(userModifyPwdParam.getNewPassword(), rsaConfig.getKeyPair().getPrivate());
        } catch (Exception e) {
            throw new EncryptException("密码非用户输入密码");
        }
        if (!passwordEncoder.matches(oldPwd, userDO.getPassword())) {
            throw new BusinessException("旧密码错误");
        }
        return modifyPassword(id, newPwd);
    }

    @Override
    public Boolean findPwd(UserFindPwdParam userFindPwdParam) {
        UserDO userDO = userMapper.selectOne(new LambdaQueryWrapper<>(UserDO.class)
                .eq(UserDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .eq(UserDO::getMobile, userFindPwdParam.getPhone()));
        if (null == userDO) {
            throw new BusinessException("用户不存在");
        }

        ResponseResult<Boolean> responseResult = msgClient.checkVerificationCode(userFindPwdParam.getKey(), userFindPwdParam.getPhone(), userFindPwdParam.getVerificationCode());
        if (null == responseResult || null == responseResult.getData() || !responseResult.getData()) {
            throw new BusinessException("验证码错误或已过期");
        }
        String newPwd;
        try {
            newPwd = EncryptUtils.RsaEncrypt.decrypt(userFindPwdParam.getNewPassword(), rsaConfig.getKeyPair().getPrivate());
        } catch (Exception e) {
            throw new EncryptException("密码非用户输入密码");
        }
        return modifyPassword(userDO.getId(), newPwd);
    }

    @Override
    public List<CurrentPermissionVO> currentPermission(CurrentUser currentUser, String jti) {
        return permissionServiceImpl.queryEffectiveList(currentUser, jti);
    }

    @Override
    public List<String> queryRoleIdsByUserId(Long userId) {
        List<GmcUserRoleBO> gmcUserRoleBOS = userRoleServiceImpl.queryUserRoleByUserId(userId);
        if (Collections3.isEmpty(gmcUserRoleBOS)) {
            return Collections3.emptyList();
        }
        return gmcUserRoleBOS.parallelStream().map(userRoleBO -> String.valueOf(userRoleBO.getRoleId())).distinct().collect(Collectors.toList());
    }

    @Override
    public Boolean checkAdministrator(Long userId) {
        List<GmcUserRoleBO> gmcUserRoleBos = userRoleServiceImpl.queryUserRoleByUserId(userId);
        if (Collections3.isEmpty(gmcUserRoleBos)) {
            return false;
        }
        Long administratorId = roleServiceImpl.selectAdministratorId();
        List<Long> userRoleIds = gmcUserRoleBos.parallelStream().map(GmcUserRoleBO::getRoleId).collect(Collectors.toList());
        return userRoleIds.contains(administratorId);
    }

    @Override
    public Boolean modifyMobile(Long userId, ModifyMobileParam param) {
        ResponseResult<Boolean> responseResult = msgClient.checkVerificationCode(param.getKey(),param.getMobile(), param.getVerificationCode());
        if (null == responseResult || null == responseResult.getData() || !responseResult.getData()) {
            throw new BusinessException("验证码错误或已过期");
        }
        UserDO userDO = new UserDO();
        userDO.setId(userId);
        userDO.setMobile(param.getMobile());
        userDO.setModifyTime(LocalDateTime.now());
        return userMapper.updateById(userDO) > 0;
    }

    private Boolean modifyPassword(Long userId, String newPassword) {
        UserDO userDO = new UserDO();
        userDO.setId(userId);
        userDO.setModifyTime(LocalDateTime.now());
        userDO.setPassword(StringUtils.isEmpty(newPassword) ? passwordEncoder.encode(userConfigProp.getInitialPwd()) : passwordEncoder.encode(newPassword));
        return userMapper.updateById(userDO) > 0;
    }

    private IGmcUserService getBean() {
        return SpringBeanUtil.getBean(IGmcUserService.class);
    }
}
